#!/usr/bin/env python

import sys
import itertools

try:
    import sys, os, pygtk
    pygtk.require('2.0')
    import gtk
    import pango
except:
    print 'You need pygtk (python-gtk2 on ubuntu) and >=gtk-2.16 installed'
    raise

import evalcore, consoleapp

fname = os.path.join(os.path.dirname(sys.argv[0]), 'interface.xml')


def flatten(iterable):
    it = iter(iterable)
    for i in it:
        if isinstance(i, (list, tuple)):
            for f in flatten(i):
                yield f
        else:
            yield i


class OutputWriter:

    def write(self, message):
        app.output_append(message)

    def isatty(self):
        return False


class Gui:
    def on_window_destroy(self, widget, data=None):
        gtk.main_quit()

    def get_filename(self):
        dialog = gtk.FileChooserDialog("Select file",
            self.window, gtk.FILE_CHOOSER_ACTION_OPEN,
            (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
            gtk.STOCK_OPEN, gtk.RESPONSE_OK))
        dialog.set_default_response(gtk.RESPONSE_OK)
        response = dialog.run()
        if response == gtk.RESPONSE_OK:
            filename = dialog.get_filename()
        else:
            filename = None
        dialog.destroy()
        return filename

    def show_message(self, message):
        md = gtk.MessageDialog(self.window,
            gtk.DIALOG_DESTROY_WITH_PARENT,
            gtk.MESSAGE_INFO,
            gtk.BUTTONS_CLOSE,
            message)
        md.run()
        md.destroy()

    def throw_error(self, message):
        md = gtk.MessageDialog(self.window,
            gtk.DIALOG_DESTROY_WITH_PARENT,
            gtk.MESSAGE_ERROR,
            gtk.BUTTONS_CLOSE,
            message)
        md.run()
        md.destroy()
        #error_dialog.connect('response', gtk.Widget.destroy)
        #error_dialog.show()
    
    def sync_gui(self):
        while gtk.events_pending() or gtk.gdk.events_pending():
            gtk.main_iteration_do(True)


    def show_evaluate_dialog(self):
        dialog = gtk.FileChooserDialog("Select file to evaluate",
            self.window, gtk.FILE_CHOOSER_ACTION_OPEN,
            (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
            gtk.STOCK_OPEN, gtk.RESPONSE_OK))
        dialog.set_default_response(gtk.RESPONSE_OK)
        vbox = gtk.VBox()
        table = gtk.Table(2, 2)
        #table.set_row_spacings(8)
        table.set_col_spacings(15)

        entry1 = gtk.Entry()
        label = gtk.Label('Name of the problem:')
        label.set_property('xalign', 0.0)
        table.attach(label, 0, 1, 0, 1, xoptions=gtk.FILL, yoptions=0)
        table.attach(entry1, 1, 2, 0, 1, ypadding = 5)
        entry2 = gtk.Entry()
        table.attach(gtk.Label('Path where problem\'s tests are:'), 0, 1, 1, 2, xoptions=gtk.FILL, yoptions=0)
        table.attach(entry2, 1, 2, 1, 2, ypadding = 5)

        label = gtk.Label("Leave blank if problem's name is same as executable's")
        label.set_property('xalign', 0.0)
        vbox.pack_start(label, False, False)
        vbox.pack_start(gtk.HSeparator())
        vbox.pack_start(table)
        checkbutton = gtk.CheckButton('Try to deduce path automatically even if both name and path are specified')
        checkbutton.set_active(True)
        vbox.pack_end(checkbutton, False, False)
        vbox.show_all()

        dialog.set_extra_widget(vbox)
        response = dialog.run()
        if response == gtk.RESPONSE_OK:
            filename = dialog.get_filename()
        else:
            filename = None
        name = entry1.get_text()
        path = entry2.get_text()
        configure = checkbutton.get_active()
        dialog.destroy()
        vbox.destroy()
        if not filename:
            return
        if not name and not path:
            name = os.path.basename(filename)
        return (filename, name, path, configure)

    def on_about_menu_item_activate(self, widget, data=None):
        pass

    def on_new_menu_item_activate(self, widget, data=None):
        pass

    ## -------------------------------------------------------------------
    ## THE EVALUATE MENU ITEM
    ## -------------------------------------------------------------------

    def on_evaluate_one_menu_item_activate(self, widget, data=None):

        loc = self.show_evaluate_dialog()
        filename, name, path, configure = loc
        #filename, name, path, configure = '/home/dan/p/infoarena/virus', 'virus', '', False

        # creating evaluation object & setting init parameters
        self.output_append('Evaluating executable: `%s\'\n' % filename)
        try:
            problema = evalcore.Problem(name = name, path = path, configure = configure)
        except evalcore.EvalError, err:
            self.throw_error("Error loading file %s into the evaluator:\n(problem name = %s)\n(path to test directory = %s)\n\n%s" %
                (filename, name, path, str(err)))
            return
        self.output_append('Problem: %s\n' % problema.path)

        # Change to 'Results' page
        # TODO: change to 1 when 'Problems' will be reintroduced
        self.ref['notebook'].set_current_page(0)
        ls = self.ref['tests']
        ls.clear()
        self.sync_gui()
        # START EVALUATING...
        try:
            self.do_evaluate(problema, filename)
        except evalcore.EvalError, err:
            self.throw_error("Error while evaluating: \n%s" %
                str(err))
        finally:
            self.output_append('='*80 + '\n')

    # ----------------------------------------------------------------------

    def do_evaluate(self, problema, filename):
        score = real_score = 0
        ls = self.ref['tests']

        tester = problema.evaluate(filename)
        # use consoleapp.evaluate_tester (problema, params, ...)  for output
        console_eval = consoleapp.evaluate_tests_iter(problema) # more?

        for i, j in itertools.izip_longest(tester, console_eval):
            console_eval.send(i)
            if i is None: break
            eval_status, d = i
        #for eval_status, d in tester:

            # state, test, verdict, score, time, memory, evalmsg, color, icon_name
            expected = ['state', 'test', 'verdict', 'time', 'memory', 'evalmsg', 'color', 'icon_name', 'score']
            colors = ['#B0ECAE', '#FFAAA4']
            #color = gtk.gdk.color_parse(colorcode)
            icon_names = ['emblem-default', 'dialog-error', 'applications-system']
            state = ( d.get('score') == 100 and (0,) or (1,) )[0]
            mapping = {
                'state': state,
                'test': d['id'],
                'verdict': d['verdict'],
                'score': d.get('real_score', ''),
                'time': d['time'],
                'memory': d['memory'],
                'evalmsg': d['jrun_message'],
                'color': colors[state],
                'icon_name': icon_names[state],
            }
            line = map(mapping.get, expected)

            # Append new line and refresh gui
            iter = ls.append()
            ls.set( iter, *list(flatten(enumerate(line))) )
            self.sync_gui()

            # Append it to console too
            '''
            lens = [12, 20, 10, 10, 10, 10]
            order = ['id', 'verdict', 'real_score', 'time', 'memory', 'jrun_message']
            line = map(d.get, order)
            line = [str(i) for i in line if i is not None]
            line[0] = 'Test %s' % line[0]
            line = [j.ljust(lens[i]) for i, j in enumerate(line)]

            self.output_append(''.join(line) + '\n')
            '''

        # TODO: move calculating of actual score to evalcore.py
        punctaj = eval_status['real_score'] or \
            int(round(eval_status['score'] / eval_status['tests_total']))

        '''self.output_append('Total score: %d\n' % punctaj)'''
        self.show_message('Evaluation completed.\n Score: %d points' % punctaj)


    def _setup_scroll_to_bottom(self):
        buf = self.output_buffer
        iter = buf.get_end_iter()
        buf.create_mark("scroll", iter, True)
        #return gobject.timeout_add(100, self.scroll_to_bottom, self.ref['textview_output'])


    def output_append(self, message):
        buf = self.output_buffer
        iter = buf.get_end_iter()

        buf.insert(iter, message)
        # Move iterator to beginning of line
        iter.set_line_offset(0)
        mark = buf.get_mark("scroll")
        buf.move_mark(mark, iter)

        # Scroll to it
        self.ref['textview_output'].scroll_mark_onscreen(mark)
        return True



    def _create_cell_renderers(self, builder):
        column = builder.get_object('verdict')
        cell = gtk.CellRendererText()
        column.pack_start(cell, True)
        cell.set_property('xalign', 0.5)
        cell.set_property('weight', 800)
        column.add_attribute(cell, 'text', 2)
        column.add_attribute(cell, 'cell-background', 6)

        '''
        cell = gtk.CellRendererText()
        column = gtk.TreeViewColumn('HEADER', cell)
        cell.set_property('xalign', 0.5)
        cell.set_property('text', 'ehna')
        builder.get_object('treeview').append_column(column)
        '''


    def __init__(self):

        self.ref = {}

        builder = gtk.Builder()
        builder.add_from_file(fname)
        builder.connect_signals(self)

        self.window = builder.get_object('window')
        items = ['statusbar', 'textview_output', 'tests', 'notebook']
        for i in items:
            self.ref[i] = builder.get_object(i)

        self.ref['statusbar'].push(0, 'Ready')

        self._create_cell_renderers(builder)

        self.output_buffer = self.ref['textview_output'].get_buffer()
        self.ref['textview_output'].modify_font(pango.FontDescription("Courier New 8"))
        #self.ref['textview_output'].set_buffer(self.output_buffer)
        self.ref['textview_output'].set_property('editable', False)

        self._setup_scroll_to_bottom()

        # We now use setup_output here ;)
        output = OutputWriter()
        # just errors and notices. although 1 is not implemented yet as "errors"
        evalcore.setup_output(output_to = output, log_output_to = output, verbosity = 3)



if __name__ == '__main__':
    app = Gui()
    app.window.show()
    gtk.main()
